class GameCharacter extends MonoBehaviour
{
	//References
	protected var _mapReference:Map;
	protected var _characterManager:CharacterManager;
	protected var _tileReference:Tile;
	
	//Turns
	protected var MAX_TURNS:int = 2;
	protected var turnsLeft:int; //decision number. each unit is granted 2 moves. some moves are restricted in second turn
	
	protected var myHealth:int = 100;
	protected var isDone:boolean;
	protected var isDead:boolean = false;
	protected var deadAnimationFinished:boolean = false;
	
	//Hovering GUI
	protected var maxHealth:int = 100;
	protected var screenPosition:Vector3; 
	protected var healthBarLength:float;
	protected var healthPercent:float;
	protected var style:GUIStyle;
	protected var backStyle:GUIStyle;
	protected var texture:Texture2D;
	protected var backTexture:Texture2D;
	
	//Label
	protected var fontstyle:GUIStyle;
	protected var toDisplayOverlayText :boolean;
	
	//Name of character
	protected var characterName:String;
	
	//Animation
	protected var isExecuting:boolean;
	
	private var isSelected:boolean;
	protected var isPlayerCharacter:boolean; //set is player or AI
	
	//Sight Radius
	protected var sightRadius:int = 10;
	protected var moveRadius:int = 7;
	
	//Attack radius
	protected var attackRadius = 9;
	protected var attackDamage = 30;
	protected var actualDamage = 10;
	protected var attackChanceModifier = 0;
	protected var skillRadius = 0;
	
	//Skills
	protected var _maxSkillCooldown:int;
	protected var _skillCooldown:int;
	protected var _isCooldown:boolean;
		
	//whether the character moved
	protected var _hasMoved:boolean;
	protected var _path:Array;
	protected var _destTile:Tile;
	protected var _destPos:Vector3;
	protected var _destReached:boolean;
	protected var _directionV:Vector3;
	protected var _speedMultiplier:float;
	
	enum PlayerType { PLAYER_MELEE, PLAYER_ARCHER, PLAYER_HEALER, PLAYER_SPELL, PLAYER_SCOUT }
	protected var _playerType:PlayerType;
	
	//Visibility
	protected var _visible:boolean;
	protected var _isSelectable:boolean;
	protected var _attackableText:GameObject = null;
	protected var _visibleToEnemy:boolean;
	
	//Coordinates
	
	//Pause mode
	protected var pauseMode = false;
	
	//Buttons clicked
	public static var ATK_BTN:int = 1;
	public static var MOVE_BTN:int = 2;
	public static var PKB_BTN:int = 3;
	public static var NTH_BTN:int = 4;
	public static var SKILL_BTN:int = 5;
	public static var CANCEL_BTN:int = 6;
	public static var NO_BTN:int = 0;
	protected var btnClicked:int;
		
	//Buttons GUI	
	protected var textureRatio:float = 1.0;	
	protected var buttonMoveTexture:Texture2D;
	protected var buttonMoveHoverTexture:Texture2D;
	protected var buttonMovePressedTexture:Texture2D;
	protected var movestyle:GUIStyle; 
	
	protected var buttonAttackTexture:Texture2D;
	protected var buttonAttackHoverTexture:Texture2D;
	protected var buttonAttackPressedTexture:Texture2D;
	protected var attackstyle:GUIStyle;
	
	protected var buttonPeekTexture:Texture2D;
	protected var buttonPeekHoverTexture:Texture2D;
	protected var buttonPeekPressedTexture:Texture2D;	
	protected var peekstyle:GUIStyle;
	
	protected var buttonEndTexture:Texture2D;
	protected var buttonEndHoverTexture:Texture2D;
	protected var buttonEndPressedTexture:Texture2D;	
	protected var endstyle:GUIStyle;
	
	protected var buttonCancelTexture:Texture2D;
	protected var buttonCancelHoverTexture:Texture2D;
	protected var buttonCancelPressedTexture:Texture2D;	
	protected var cancelstyle:GUIStyle;
	
	protected var buttonSkillTexture:Texture2D;
	protected var buttonSkillHoverTexture:Texture2D;
	protected var buttonSkillPressedTexture:Texture2D;	
	protected var skillstyle:GUIStyle;
	
	protected var coverTexture:Texture2D;
	private var customGuiStyle : GUIStyle;
	
	//Animation
	protected var STOP_ANI:int = 0;
	protected var WALK_ANI:int = 1;
	protected var JUMP_ANI:int = 2;
	protected var SKILL_ANI:int = 3;
	protected var COVER_IN_ANI:int = 4;
	protected var ATTACK_ANI:int = 5;
	protected var _currentAniState:int = STOP_ANI;
	
	//Peekaboo
	protected var _peekabooTarget:EnemyCharacter;
	protected var _peekabooEnabled:boolean;
	protected var _peekabooCount:int;
	protected var _peekabooLock:boolean;
	
	//Sound
	protected var buttonSound:AudioClip;
	protected var selectedSound:AudioClip;
	protected var walkSound:AudioClip;
	protected var jumpSound:AudioClip;
	protected var bowSound:AudioClip;
	protected var endturnSound:AudioClip;
	protected var peekabooSound:AudioClip;
	protected var WALK_VOLUME:float = 0.25f;

	public function GameCharacter()
	{
		isDone = false;
		isDead = false;
		toDisplayOverlayText = false;
		
		textureRatio = (Screen.width*1.0/1920);
		
		turnsLeft = MAX_TURNS;
		btnClicked = NO_BTN;
		_peekabooEnabled = false;
		_peekabooCount = 0;
		
		isExecuting = false;
		isSelected = false;
		_visible = true;
		_visibleToEnemy = true;
		_hasMoved = true;
		_path = new Array(0);
		_destReached = true;
		_destTile = null;
		characterName = "Character";
		//fontstyle.normal.textColor = Color.red;
		
		healthPercent = 100.0;
		healthBarLength = Screen.width/9; //JUSTIN
		style = new GUIStyle();
	    texture = new Texture2D(1, 1);
		backTexture = new Texture2D(1, 1);
			
    	for (var y:int = 0; y < texture.height; ++y)
	    {
	    	for (var x:int = 0; x < texture.width; ++x)
	        {
	            backTexture.SetPixel(x, y, Color.gray);
	        }
	    }
    	backTexture.Apply();
    	backStyle = new GUIStyle();    
    	backStyle.normal.background = backTexture;
		
		//Skills
		_maxSkillCooldown = 2;
		_skillCooldown = 0;
		_isCooldown = false;
		_isSelectable = false;
		_attackableText = null;	
		
	 	buttonAttackTexture = Resources.Load("Buttons/attack_buttonNormal");
	 	buttonAttackHoverTexture = Resources.Load("Buttons/attack_buttonHover");
	 	buttonAttackPressedTexture = Resources.Load("Buttons/attack_buttonPressed");
	 	
	 	buttonMoveTexture = Resources.Load("Buttons/move_buttonNormal");
	 	buttonMoveHoverTexture = Resources.Load("Buttons/move_buttonHover");
	 	buttonMovePressedTexture = Resources.Load("Buttons/move_buttonPressed");
	 		 	
	 	buttonPeekTexture = Resources.Load("Buttons/peek_buttonNormal");
	 	buttonPeekHoverTexture = Resources.Load("Buttons/peek_buttonHover");
	 	buttonPeekPressedTexture = Resources.Load("Buttons/peek_buttonPressed");
	 		 	
	 	buttonEndTexture = Resources.Load("Buttons/end_buttonNormal");
	 	buttonEndHoverTexture = Resources.Load("Buttons/end_buttonHover");
	 	buttonEndPressedTexture = Resources.Load("Buttons/end_buttonPressed");	 	
	 	
	 	buttonCancelTexture = Resources.Load("Buttons/cancel_buttonNormal");
	 	buttonCancelHoverTexture = Resources.Load("Buttons/cancel_buttonHover");
	 	buttonCancelPressedTexture = Resources.Load("Buttons/cancel_buttonPressed");	 
	 	
	 	coverTexture = Resources.Load("cover");
	 	
	 	/*buttonSkillTexture = Resources.Load("Buttons/skill_buttonNormal");
	 	buttonSkillHoverTexture = Resources.Load("Buttons/skill_buttonHover");
	 	buttonSkillPressedTexture = Resources.Load("Buttons/skill_buttonPressed");	*/
	 	
		attackstyle = new GUIStyle();
		attackstyle.normal.background = buttonAttackTexture;
		attackstyle.hover.background = buttonAttackHoverTexture;
		attackstyle.active.background = buttonAttackPressedTexture;

		movestyle = new GUIStyle();
		movestyle.normal.background = buttonMoveTexture;
		movestyle.hover.background = buttonMoveHoverTexture;
		movestyle.active.background = buttonMovePressedTexture;
		
		peekstyle = new GUIStyle();
		peekstyle.normal.background = buttonPeekTexture;
		peekstyle.hover.background = buttonPeekHoverTexture;
		peekstyle.active.background = buttonPeekPressedTexture;
		
		/*skillstyle = new GUIStyle();
		skillstyle.normal.background = buttonSkillTexture;
		skillstyle.hover.background = buttonSkillHoverTexture;
		skillstyle.active.background = buttonSkillPressedTexture;*/
		
		endstyle = new GUIStyle();
		endstyle.normal.background = buttonEndTexture;
		endstyle.hover.background = buttonEndHoverTexture;
		endstyle.active.background = buttonEndPressedTexture;		
		
		buttonSound = Resources.Load("buttonSound2");	
		walkSound = Resources.Load("walkSound");	
		jumpSound = Resources.Load("jumpSound");
		bowSound = Resources.Load("bowSound");	
		endturnSound = Resources.Load("playerendturnSound");
		peekabooSound = Resources.Load("peekSound");
		
		var myFont:Font = Resources.Load("PineappleDemo");
		customGuiStyle = new GUIStyle();
 		customGuiStyle.fontSize = 15;
 		customGuiStyle.font = myFont;
 		customGuiStyle.normal.textColor = Color.white;			
	}
	
	public function initAnimation()
	{
		setAnimationState(WALK_ANI);
		setAnimationState(STOP_ANI);
	}
	//OTHER FUNCTIONS
	public function setProjectileReached(reached:boolean)
	{	
		if(reached)
			_characterManager.setMoveDone(true);
	}
	
	//used by CharacterManager to reset the character
	public function resetTurn()
	{
		btnClicked = NO_BTN;
		turnsLeft = MAX_TURNS;
		isDone = false;
		_peekabooEnabled = false;
	}
	//used locally to end this character's turn and tell CharacterManager
	public function endTurn()
	{
		turnsLeft = 0;
		isSelected = false;
		isDone = true;	
		_isSelectable = false;
		
		if(_skillCooldown > 0){
			_skillCooldown--;
		}else if(_skillCooldown == 0){
			_isCooldown = false;
		}		
		
		yield WaitForSeconds(2.0);
		
		returnToCoverAnimation();
			
		_characterManager.characterEnd();
	}
	
	public function returnToCoverAnimation()
	{
		if (_tileReference.isCover())
			setAnimationState(COVER_IN_ANI);
	}
		
	
	//reset the highlighted tiles
	public function resetTilesHighlight()
	{
	
	}
	
	//used by CharacterManager to clear the button clicked
	function removeBtnClicked()
	{
		btnClicked = NO_BTN;
	}
	
	function Selected()
	{
		audio.PlayClipAtPoint(selectedSound,Vector3(0,0,0));	
		isSelected = true;
	}
	
	//Empty function. Implemented by player
	function Deselected() {
		isSelected = false;
	}
	
	public function attack(target:EnemyCharacter, projectileType:int)
	{
		//calculate actual atk damage
		actualDamage = calculateActualDamage(target);
		
		//rotate to face the target
		var directionOfAttack:Vector3 = target.getSurfaceCoord() - this.getSurfaceCoord();
		directionOfAttack.y = 0;
		this.transform.rotation = Quaternion.LookRotation(directionOfAttack);	
		
		audio.PlayClipAtPoint(bowSound,Vector3(0,0,0));
		
		//play attack animation
		setAnimationState(ATTACK_ANI);
	}
	
	//OVERRIDE THIS FUNCTION IN EVERY INHERITED CLASS
	public function calculateHitChance(target:EnemyCharacter)
	{
		//currently calculating based on an average atk
		//calculate distance of enemy
		var distance = Vector3.Distance(target.getSurfaceCoord(), this.getSurfaceCoord());
		//based on the maximum range of weapon, calculate the hit chance based on the optimized range
		//close - mid range weapon
		var optimizedRangeMax = 5;
		var hitChance = 100;
		//too far
		if (distance > optimizedRangeMax)
			hitChance -= (distance - optimizedRangeMax) * 20;
		//if in cover
		if (target.isInCover())
		{
			if (!target.isFlanked(this))
				hitChance -= Obstacle.HIT_CHANCE_REDUCTION;
		}
		
		//check height advantage
		var heightDifference = target.getSurfaceCoord().y - this.getSurfaceCoord().y;
		if (heightDifference > 0)
			hitChance -= heightDifference * 10;
		else if (heightDifference < 0)
			hitChance += heightDifference * -10;
		
		//minimum 10%
		if (hitChance < 10)
			hitChance = 10;
		else if (hitChance > 100)
			hitChance = 100;
		return hitChance;
	}
	
	public function calculateActualDamage(target:EnemyCharacter)
	{
		//if in cover
		if (target.isInCover())
		{
			//check whether flanked
			if (target.isFlanked(this))
			{
				return attackDamage *2; 
			}
			else
			{
				return attackDamage;
			}
		}
		//not in cover
		else
		{
			return attackDamage *2;
		}
	}
	
	public function skill(target:GameCharacter) //even fire ball must aim for person
	{
	
	}
	public function skill(target:EnemyCharacter) //even fire ball must aim for person
	{
	
	}
	public function skill(){}
	
	protected function startCooldown(){
		_isCooldown = true;
		_skillCooldown = _maxSkillCooldown;
	}
	
	//UPDATE FUNCTIONS
	function Update () {
		//skip update if pause
		if (pauseMode)
			return;
		//skip update if dead 
		if (isDead)
		{
			if (deadAnimationFinished)
				return;
			else
			{
				this.transform.position.y -= 0.003;
				if (this.transform.position.y < -2)
					deadAnimationFinished = true;
				return;
			}
		}
		
		//if not at destination
		if (!_destReached && !_peekabooLock)
		{
			var currPos:Vector3 = this.transform.position;
			if (Vector3.Distance(currPos, _destPos) <= 0.1)
			{
				//reached destination
				if (_path.length != 0)
				{					
					_destTile = _path.Shift();
					_destPos = _destTile.getSurfaceCoord();
					_destPos.y += this.renderer.bounds.size.y/2;
				
					//not reached yet. move towards it
					_directionV = _destPos - currPos;
					_directionV.Normalize();
					//remove the rotation for height
					var lookRotation:Vector3 = new Vector3(_directionV.x, 0, _directionV.z);
					this.transform.rotation = Quaternion.LookRotation(lookRotation);
					
					//play animation
					if (_destTile.getHeight() == _tileReference.getHeight())
					{
						audio.PlayClipAtPoint(walkSound,Vector3(0,0,0), WALK_VOLUME);
						setAnimationState(WALK_ANI);
						_speedMultiplier = 0.05;
					}
					else
					{
						audio.PlayClipAtPoint(jumpSound,Vector3(0,0,0));
						setAnimationState(JUMP_ANI);
						_speedMultiplier = 0.025;
					}
				}
				else
				{
					//reduce turn after move has finihsed
					turnsLeft--;
					if (turnsLeft <= 0){
						endTurn();
						
					}
					_destReached = true;
					_characterManager.setMoveDone(true);
					//play animation
					if (_destTile.isCover())
						setAnimationState(COVER_IN_ANI);
					else
						setAnimationState(STOP_ANI);
				}
				_hasMoved = true;
				
				this.setWorldCoord();
				peekabooCheck();
				_tileReference.clearCharacter();
				this.setTileReference(_destTile);
			}
			else
			{
				this.transform.position += _directionV * _speedMultiplier;
				
			}
		}
	}
	
	function OnGUI () {
    
    	if( pauseMode || isDead ) 
    		return;
    
		if( toDisplayOverlayText || isSelected)
		{
			//calculate rotation for GUI to face camera
			screenPosition = Camera.main.WorldToScreenPoint(transform.position);
    		screenPosition.y = Screen.height - (screenPosition.y + 1); 
			//Display player attributes
    		GUI.Label(Rect(screenPosition.x - (healthBarLength/2), screenPosition.y - 94,100,50),characterName, customGuiStyle);//,fontstyle);
			GUI.Label(Rect(screenPosition.x - (healthBarLength/2), screenPosition.y - 80,100,50), turnsLeft+" moves", customGuiStyle);//,fontstyle);			
			GUI.Label(Rect(screenPosition.x - (healthBarLength/2), screenPosition.y - 55,100,50), myHealth+"/"+maxHealth, customGuiStyle);//fontstyle);	
			//show whether tile is cover
			//GUI.Label(Rect(screenPosition.x - (healthBarLength/2), screenPosition.y - 106,100,50),"In cover: " + _tileReference.isCover());//,fontstyle);
			
			if(_tileReference.isCover())
				GUI.DrawTexture(Rect(screenPosition.x - (healthBarLength/2) - 35, screenPosition.y - 85, 30, 30), coverTexture); 
			
			if(attackChanceModifier < 0)
				GUI.Label(Rect(screenPosition.x - (healthBarLength/2), screenPosition.y - 110,100,50),"Blinded", customGuiStyle);//,fontstyle);
			
			displayHealthBar();
		}
		
	
		if( isSelected && !isDone )
		{	
			//nothing clicked yet, show all buttons
			if (btnClicked == NO_BTN)
			{	
				var spacing:float = 212*textureRatio;
			
				//Display actions
				if (GUI.Button(Rect(spacing*3,Screen.height-100,212*textureRatio,85*textureRatio),null,attackstyle))
				{
					audio.PlayClipAtPoint(buttonSound,Vector3(0,0,0));
					btnClicked = ATK_BTN;
				}
				if (GUI.Button(Rect(spacing*4,Screen.height-100,212*textureRatio,85*textureRatio),null,skillstyle))
				{
					audio.PlayClipAtPoint(buttonSound,Vector3(0,0,0));
					btnClicked = SKILL_BTN;					
				}
				if (GUI.Button(Rect(spacing*5,Screen.height-100,212*textureRatio,85*textureRatio),null,movestyle))
				{
					audio.PlayClipAtPoint(buttonSound,Vector3(0,0,0));
					//if haven't highlighted ground before, hightlight it
					if (btnClicked != MOVE_BTN)
					{
						_mapReference.checkRangeFrom(_tileReference.getCoord(), moveRadius);
						_mapReference.highlightTiles(_tileReference.getCoord(), moveRadius);
					}
					btnClicked = MOVE_BTN;
					
				}
				if (GUI.Button(Rect(spacing*6,Screen.height-100,212*textureRatio,85*textureRatio),null,peekstyle))
				{
					audio.PlayClipAtPoint(buttonSound,Vector3(0,0,0));
					btnClicked = PKB_BTN;
					Debug.Log("Clicked Peek");
					setPeekabooEnabled(true);
					
					audio.PlayClipAtPoint(peekabooSound,Vector3(0,0,0));
					
					endTurn();
				}				
				if (GUI.Button(Rect(spacing*7,Screen.height-100,212*textureRatio,85*textureRatio),null,endstyle))
				{
					audio.PlayClipAtPoint(buttonSound,Vector3(0,0,0));
					btnClicked = NTH_BTN;
					
					audio.PlayClipAtPoint(endturnSound,Vector3(0,0,0));
					
					endTurn();
				}
			}
			//display a cancel button if a button was clicked
			else if (btnClicked != NTH_BTN && btnClicked != PKB_BTN)
			{
				var cancelstyle:GUIStyle = new GUIStyle();
				cancelstyle.normal.background = buttonCancelTexture;
				cancelstyle.hover.background = buttonCancelHoverTexture;
				cancelstyle.active.background = buttonCancelPressedTexture;
			
				spacing = 212*textureRatio;
			
				if (GUI.Button(Rect(spacing*5,Screen.height-100,212*textureRatio,85*textureRatio),null,cancelstyle))
				{
					audio.PlayClipAtPoint(buttonSound,Vector3(0,0,0));
					if (btnClicked == MOVE_BTN){
						_mapReference.unhighlightTiles();
					}else if (btnClicked == ATK_BTN || btnClicked == SKILL_BTN){
						_characterManager.unhighlightEnemies();
						_characterManager.unhighlightPlayers();
					}
					
					
					_characterManager.clearPlayerSelectionLock();
					btnClicked = NO_BTN;
				}
			}
    	}
		
	}
	
	//MOVING FUNCTIONS
	public function moveTo(tile:Tile):void{
		_mapReference.unhighlightTiles();
		_path = _mapReference.getPath(tile.getCoord());
		_path.Shift();
		_destTile = _path.Shift();
		_destPos = _destTile.getSurfaceCoord();
		_destPos.y += this.renderer.bounds.size.y/2;
	
		_directionV = _destPos - this.transform.position;
		_directionV.Normalize();
		
		//remove the rotation for height
		var lookRotation:Vector3 = new Vector3(_directionV.x, 0, _directionV.z);
		this.transform.rotation = Quaternion.LookRotation(lookRotation);
		_destReached = false;
		
		_tileReference.clearCharacter();
		this.setTileReference(_destTile);
		
		//play animation
		if (_directionV.y == 0)
		{
			audio.PlayClipAtPoint(walkSound,Vector3(0,0,0), WALK_VOLUME);
			setAnimationState(WALK_ANI);
			_speedMultiplier = 0.05;
		}
		else
		{
			audio.PlayClipAtPoint(jumpSound,Vector3(0,0,0));
			setAnimationState(JUMP_ANI);
			_speedMultiplier = 0.025;
		}
	
	}
	public function isWithinMoveRange(tile:Tile)
	{
		return _mapReference.checkReachable(tile.getCoord());
	}	

	//MOUSE FUNCTIONS
	function OnMouseOver () {
	}
	function OnMouseExit () {
		toDisplayOverlayText = false;
	}
	
	function OnMouseEnter(){
		toDisplayOverlayText = true;
		_tileReference.highlightMe();
	}
	
	function OnMouseDown()
	{
		//can select this player
		if (turnsLeft > 0 && _characterManager.setSelectedPlayer(this))
		{
			//if not previously selected
			if (!isSelected)
			{
				this.Selected();
			}
		}
		if (_isSelectable)
			_characterManager.setSelectedPlayer(this);
	}
	
	public function setSelectable(selectable:boolean)
	{
		_isSelectable = selectable;
		if (_isSelectable)
		{
			_attackableText = Resources.Load("Attackable_text");
			_attackableText = MonoBehaviour.Instantiate(_attackableText);
			_attackableText.transform.position = _tileReference.getSurfaceCoord();
			_attackableText.transform.position.y += 2;
			
			_attackableText.GetComponent(AttackableText).setText("Heal", 50, Color.green);				
		}
		else
		{
			Destroy(_attackableText);
			_attackableText = null;
		}	
	}
	
	public function isSelectable(){ return _isSelectable; }
	
	//visibility functions
	//used mostly by the map to set enemy in the Fog invisible
	function setIsVisible(b:boolean):void
	{
		_visible = b; 
		var renderers;
		if (b)
		{
			renderers = GetComponentsInChildren(Renderer);
			for (var r : Renderer in renderers)
    			r.enabled = true;
		}
		else
		{	
			renderers = GetComponentsInChildren(Renderer);
			for (var r : Renderer in renderers)
    			r.enabled = false;
		}
	}
	
	function isVisible():boolean { return _visible; }
	function isVisibleToEnemy():boolean{ return _visibleToEnemy; }
	
	//PERSONAL GUI FUNCTIONS
	function displayHealthBar(){					
    	var oldColor:Color = GUI.color;
    	var newColor:Color = Color.green;	
		
    	if(healthPercent < 0.2){
   			newColor = Color.red;
   		}else if(healthPercent < 0.5){
   			newColor = Color.yellow;
   		}
	  
	    for (var y:int = 0; y < texture.height; ++y)
	    {
	    	for (var x:int = 0; x < texture.width; ++x)
	        {
	            texture.SetPixel(x, y, newColor);
	        }
	    }
	    texture.Apply(); 	    
    	style.normal.background = texture; 	
    	    	
    	GUI.Box(new Rect(screenPosition.x - (healthBarLength/2), screenPosition.y - 65, (Screen.width / 9), 7), "", backStyle);
    	
   		GUI.Box(new Rect(screenPosition.x - (healthBarLength/2), screenPosition.y - 65, healthBarLength, 7), "", style);

   		GUI.color = oldColor;   		  		
	}
	
	protected function increaseHealthPercent(healthPercent:int){
		var health = Mathf.Floor((healthPercent/100.0)*maxHealth);
		
		adjustCurrentHealth(health);
	}
	
	function adjustCurrentHealth(adj:int) {
        if(adj < 0){
       		var reduceHealthText = Resources.Load("Attackable_text");
			reduceHealthText = MonoBehaviour.Instantiate(reduceHealthText);
			reduceHealthText.transform.position = _tileReference.getSurfaceCoord();
			reduceHealthText.transform.position.y += 2;
			reduceHealthText.GetComponent(AttackableText).setAutoRemoveText(adj.ToString(), 35, 3);
       	}else if(adj > 0){
       		var increaseHealthText = Resources.Load("Attackable_text");
			increaseHealthText = MonoBehaviour.Instantiate(increaseHealthText);
			increaseHealthText.transform.position = _tileReference.getSurfaceCoord();
			increaseHealthText.transform.position.y += 2;
			increaseHealthText.GetComponent(AttackableText).setAutoRemoveText("+ "+adj.ToString(), 35, 3, Color.green);
       	}
       	
        myHealth += adj;
 		
        if(myHealth <= 0)
        {
        	_characterManager.increasePlayerDeathCount();
            //Destroy(gameObject);
            isDead = true;
			_tileReference.clearCharacter();
			//load cover data in case is melee character who died
			_tileReference.loadCoverData();
        }
 
        if(myHealth > maxHealth)
            myHealth = maxHealth;
        if(maxHealth < 1)
            maxHealth = 1;
            
        healthPercent = (myHealth / (maxHealth*1.0));
        healthBarLength = (Screen.width / 9) * healthPercent;
    }
    
    function isMaxHealth():boolean{
    	if(myHealth < maxHealth)
    		return false;
    	
    	return true;
    }
	//JUSTIN END
	
	//ANIMATION FUNCTIONS
	protected function setAnimationState(state:int)
	{
		var walk_model = this.transform.Find("walk").gameObject;
		var jump_model = this.transform.Find("jump").gameObject;
		var skill_model = this.transform.Find("skill").gameObject;
		var attack_model = this.transform.Find("attack").gameObject;
		var cover_in_model = this.transform.Find("toCover").gameObject;

		switch (state)
		{
			case STOP_ANI:
				switch (_currentAniState)
				{
					case JUMP_ANI:
						//don't stop any animation
						break;
					default:
						walk_model.animation.Stop();
						jump_model.animation.Stop();
						skill_model.animation.Stop();
						attack_model.animation.Stop();
						cover_in_model.animation.Stop();
				}
				break;
				
			case WALK_ANI:
				jump_model.SetActive(false);
				skill_model.SetActive(false);
				attack_model.SetActive(false);
				cover_in_model.SetActive(false);
				walk_model.SetActive(true);
				walk_model.animation.Play();
				break;
				
			case JUMP_ANI:
				walk_model.SetActive(false);
				skill_model.SetActive(false);
				attack_model.SetActive(false);
				cover_in_model.SetActive(false);
				jump_model.SetActive(true);
				jump_model.animation.Play();
				break;
				
			case SKILL_ANI:
				walk_model.SetActive(false);
				jump_model.SetActive(false);
				attack_model.SetActive(false);
				cover_in_model.SetActive(false);
				skill_model.SetActive(true);
				skill_model.animation.Play();
				break;
				
			case ATTACK_ANI:
				walk_model.SetActive(false);
				jump_model.SetActive(false);
				skill_model.SetActive(false);
				cover_in_model.SetActive(false);
				attack_model.SetActive(true);
				attack_model.animation.Play();
				break;
			
			case COVER_IN_ANI:
				walk_model.SetActive(false);
				jump_model.SetActive(false);
				skill_model.SetActive(false);
				attack_model.SetActive(false);
				if (_currentAniState != COVER_IN_ANI)
				{
					cover_in_model.SetActive(true);
					cover_in_model.animation.Play();
				}
				break;
		}
		
		_currentAniState = state;
	}
	
	
	//PEEKABOO FUNCTIONS
	public function peekabooDone()
	{
		//shift back to cover animation if its originally in cover
		if (_tileReference.isCover())
			setAnimationState(COVER_IN_ANI);
				
		//unlock target so that it can continue movement
		_peekabooTarget.peekabooLock(false);
	}
	
	public function peekabooLock(b:boolean)
	{
		if(b)
			_peekabooCount++;
		else
			_peekabooCount--;
			
		if (_peekabooCount > 0)
			_peekabooLock = true;
		else
			_peekabooLock = false;
	}
	
	public function peekabooAttack(target:EnemyCharacter)
	{
		Debug.Log("PEEKABOO");
		_peekabooTarget = target;
		//fire projectile
		audio.PlayClipAtPoint(bowSound,Vector3(0,0,0));
		setAnimationState(ATTACK_ANI);
		attack(target, Projectile.PEEKABOO);
		
		//clear peekaboo state
		_peekabooEnabled = false;
		
		//put 3d text PEEKABOO
	}
	
	protected function peekabooCheck()
	{
		//if not visible, dont bother checking
		if (!_visibleToEnemy) 
			return;
		
		var peekabooedEnemy:Array = _characterManager.getEnemyPeekabooList();
		
		//loop through all and calculate distance 
		for (var i:int = 0; i < peekabooedEnemy.length; i++)
		{
			if (!peekabooedEnemy[i].isPeekabooEnabled())
				continue;
			var currEnemyCoord = peekabooedEnemy[i].getSurfaceCoord();
			var currEnemyAtkRadius = peekabooedEnemy[i].getAttackRadius();
			
			//calculate the distance
			var euclideanDistance = currEnemyCoord - this.getSurfaceCoord();
			var distance = euclideanDistance.x * euclideanDistance.x + euclideanDistance.y * euclideanDistance.y + euclideanDistance.z * euclideanDistance.z;
			//within attack radius
			if (distance <= currEnemyAtkRadius*currEnemyAtkRadius)
			{
				//check ray cast to see if within LoS
				var raySource:Vector3 = peekabooedEnemy[i].getSurfaceCoord();
				this.gameObject.layer = LayerMask.NameToLayer("Obstacle");
				var rayDst:Vector3 = this.getSurfaceCoord();
				var hit:RaycastHit;
				if (Physics.Raycast(raySource, rayDst - raySource, hit, currEnemyAtkRadius, 1<<LayerMask.NameToLayer("Obstacle")))
				{
					if (hit.collider.gameObject.tag.Equals("PlayerCharacter"))
					{
						peekabooedEnemy[i].peekabooAttack(this);
						peekabooLock(true);
					}
				}
				this.gameObject.layer = LayerMask.NameToLayer("Player");
			}
		}
	}
	
	public function setPeekabooEnabled(enabled:boolean) { _peekabooEnabled = true; }
	public function isPeekabooEnabled() { return _peekabooEnabled; }
		

	
	

	//GETTERS
	function getCoord():Vector2 { return _tileReference.getCoord();}
	function getSurfaceCoord():Vector3 
	{ 
		var surfaceCoord = _tileReference.getSurfaceCoord();
		surfaceCoord.y += 0.5;
		return surfaceCoord;
	}
	function getTileReference():Tile { return _tileReference; }
	function isPlayer():boolean { return isPlayerCharacter;}
	//not needed by Enemy
	function getSightRadius():int { return sightRadius; }
	function getMoveRadius():int { return moveRadius;}
	function getAttackRadius():int { return attackRadius; }
	function getAttackDamage():int { return attackDamage; }
	function getActualDamage():int { return actualDamage; }
	function getIsCooldown(){ return _isCooldown; }
	//used by Map to check whether need to update Fog Of War
	function hasMoved():boolean { return _hasMoved; }
	function getPlayerType(){ return _playerType; }
	function getSkillRadius(){ return skillRadius; }
	function getIsDead(){ return isDead; }	
	function getTurnsLeft() { return turnsLeft; }
	function getAttackChanceModifier(){ return attackChanceModifier; }
	function getSkillCooldown(){
		var cooldown = _skillCooldown + 1;
		if(cooldown >= _maxSkillCooldown)
			cooldown = _maxSkillCooldown;
		
		return cooldown;
	}
	//used locally to check whether the character has stopped moving
	//function reachedDestination():boolean { return _destReached;}
	
	/*function getIsDone() {
		return isDone;
	}*/
	/*function getHealth() {
		return myHealth;
	}*/
	
	function isInCover() { return _tileReference.isCover(); }
	function getCoverDirection(direction:int) { return _tileReference.getCoverDirection(direction); }
	function isFlanked(enemy:EnemyCharacter)
	{
		var directionOfEnemy = enemy.getSurfaceCoord() - this.getSurfaceCoord();
		var isFlanked:boolean = true;
		//to the right
		if (directionOfEnemy.x > 0 )
		{
			isFlanked = !_tileReference.getCoverDirection(Tile.COVER_DIRECTION_RIGHT);
			//Debug.Log("Flanked to right:" + isFlanked);
		}
		//to the left
		else if (directionOfEnemy.x < 0 )
		{
			isFlanked = !_tileReference.getCoverDirection(Tile.COVER_DIRECTION_LEFT);
			//Debug.Log("Flanked to left:" + isFlanked);
		}
		
		//if not flanked, return false 	
		if (!isFlanked)
			return false;
		
		//to the top
		if (directionOfEnemy.z > 0)
		{
			isFlanked = !_tileReference.getCoverDirection(Tile.COVER_DIRECTION_TOP);
			//Debug.Log("Flanked to top:" + isFlanked);
		}
		else if (directionOfEnemy.z < 0)
		{
			isFlanked = !_tileReference.getCoverDirection(Tile.COVER_DIRECTION_BOTTOM);
			//Debug.Log("Flanked to bottom:" + isFlanked);
		}
		
		return isFlanked;
	}
	
	
	
	//SETTERS
	function setWorldCoord():void
	{
		var v3:Vector3 = _tileReference.getSurfaceCoord();
		v3.y += this.renderer.bounds.size.y/2;
		this.transform.position = v3;
	}
	function setTileReference(tile:Tile):void 
	{
		_tileReference = tile;
		_tileReference.setCharacter(this);
		_tileReference.setIsOccupied(true);
	}
	function setMapReference(map:Map)
	{
		_mapReference = map;
	}
	function setManagerReference(cm:CharacterManager)
	{
		_characterManager = cm;
	}
	function setIsPlayer(b:boolean):void { 
		isPlayerCharacter = b;
	}
	//not needed by Enemy
	function setSightRadius(r:int):void { sightRadius = r; }
	function setMoveRadius(r:int):void { moveRadius = r; }
	function setAttackRadius(r:int):void { attackRadius = r; }
	function setAttackDamage(d:int):void { attackDamage = d;}
	//not needed by Enemy
	function getBtnClicked():int
	{
		return btnClicked;
	}
	function setHasMoved(b:boolean):void{ _hasMoved = b;}
	function setAttackChanceModifer(_attackChanceModifier:int){ attackChanceModifier = _attackChanceModifier; }
	/*function SetIsDone(b:boolean) {
		isDone = b;
	}*/
	
	function setPause(b:boolean){ pauseMode = b; }
	
}